/*
 * ImmediateCrypt
 * Copyright (C) 2012 Giacomo Drago
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * http://giacomodrago.com/go/immediatecrypt
 * 
 */

package com.giacomodrago.immediatecrypt.gui;

import com.giacomodrago.immediatecrypt.ProgramProperties;
import com.giacomodrago.immediatecrypt.messagecipher.MessageCipher;
import com.giacomodrago.utils.gui.ClipboardManager;
import com.giacomodrago.utils.gui.Dialogs;
import com.giacomodrago.utils.gui.FixedSizeDocument;
import com.giacomodrago.utils.gui.FixedSizeListener;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Image;
import java.awt.event.*;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import javax.swing.GroupLayout.Alignment;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;


@SuppressWarnings("serial")
public final class MainWindow extends JFrame {

    private final MessageCipher messageCipher;

    
    public MainWindow(MessageCipher messageProcessor) {
        this.messageCipher = messageProcessor;
        initComponents();
        customInitComponents();
        
    }
    
    private void customInitComponents() {
        
        setTitle(ProgramProperties.get("name") + " v" + ProgramProperties.get("version"));
        labelAbout.setText(
                "<html><a href=\"#\">About " + ProgramProperties.get("name") + "</a></html>");
        
        List<Image> iconImages = new LinkedList<Image>();
        iconImages.add(new ImageIcon(
                getClass().getResource("icon_128.png")).getImage());
        iconImages.add(new ImageIcon(
                getClass().getResource("icon_48.png")).getImage());
        iconImages.add(new ImageIcon(
                getClass().getResource("icon_16.png")).getImage());
        setIconImages(iconImages);
        
        JOptionPane.setDefaultLocale(Locale.US);
        
        final int passwordMaxLength = ProgramProperties.getInt("password_max_length");
        
        FixedSizeListener fixedSizeListener = new FixedSizeListener() {
            @Override
            public void limitReached(FixedSizeDocument fixedSizeDocument) {
                Dialogs.warning(MainWindow.this,
                        "Password length is limited to " + passwordMaxLength + " characters.");
            }
        };
        
        textPassword.setDocument(new FixedSizeDocument(passwordMaxLength, fixedSizeListener));
        
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        labelOrigin = new JLabel();
        scrollOrigin = new JScrollPane();
        textAreaOrigin = new JTextArea();
        textAreaOrigin.getDocument().addDocumentListener(textAreaListener);
        buttonClearOrigin = new JButton();
        buttonPasteOrigin = new JButton();
        labelDestination = new JLabel();
        scrollDestination = new JScrollPane();
        textAreaDestination = new JTextArea();
        buttonCopyDestination = new JButton();
        labelAbout = new JLabel();
        labelPassword = new JLabel();
        buttonCrypt = new JButton();
        buttonDecrypt = new JButton();
        textPassword = new JPasswordField();

        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setLocationByPlatform(true);
        setResizable(false);
        addWindowListener(new WindowAdapter() {
            public void windowOpened(WindowEvent evt) {
                formWindowOpened(evt);
            }
        });

        labelOrigin.setDisplayedMnemonic('m');
        labelOrigin.setFont(labelOrigin.getFont().deriveFont(labelOrigin.getFont().getStyle() | Font.BOLD));
        labelOrigin.setLabelFor(textAreaOrigin);
        labelOrigin.setText("Message to encrypt/decrypt:");

        textAreaOrigin.setColumns(20);
        textAreaOrigin.setFont(new Font("Monospaced",Font.PLAIN,13));
        textAreaOrigin.setLineWrap(true);
        textAreaOrigin.setRows(5);
        textAreaOrigin.setWrapStyleWord(true);
        scrollOrigin.setViewportView(textAreaOrigin);

        buttonClearOrigin.setFont(buttonClearOrigin.getFont());
        buttonClearOrigin.setText("Clear");
        buttonClearOrigin.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                buttonClearOriginActionPerformed(evt);
            }
        });

        buttonPasteOrigin.setFont(buttonPasteOrigin.getFont());
        buttonPasteOrigin.setText("Paste here");
        buttonPasteOrigin.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                buttonPasteOriginActionPerformed(evt);
            }
        });

        labelDestination.setDisplayedMnemonic('r');
        labelDestination.setFont(labelDestination.getFont().deriveFont(labelDestination.getFont().getStyle() | Font.BOLD));
        labelDestination.setLabelFor(textAreaDestination);
        labelDestination.setText("Result:");

        textAreaDestination.setColumns(20);
        textAreaDestination.setEditable(false);
        textAreaDestination.setFont(new Font("Monospaced",Font.PLAIN,13));
        textAreaDestination.setLineWrap(true);
        textAreaDestination.setRows(5);
        textAreaDestination.setWrapStyleWord(true);
        textAreaDestination.setOpaque(false);
        textAreaDestination.addFocusListener(new FocusAdapter() {
            public void focusGained(FocusEvent evt) {
                textAreaDestinationFocusGained(evt);
            }
        });
        scrollDestination.setViewportView(textAreaDestination);

        buttonCopyDestination.setFont(buttonCopyDestination.getFont());
        buttonCopyDestination.setText("Copy contents to clipboard");
        buttonCopyDestination.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                buttonCopyDestinationActionPerformed(evt);
            }
        });

        labelAbout.setFont(labelAbout.getFont());
        labelAbout.setText("About ProgramName");
        labelAbout.setCursor(new Cursor(Cursor.HAND_CURSOR));
        labelAbout.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent evt) {
                labelAboutMouseClicked(evt);
            }
        });

        labelPassword.setDisplayedMnemonic('p');
        labelPassword.setFont(labelPassword.getFont());
        labelPassword.setLabelFor(textPassword);
        labelPassword.setText("Password:");

        buttonCrypt.setFont(buttonCrypt.getFont().deriveFont(buttonCrypt.getFont().getStyle() | Font.BOLD));
        buttonCrypt.setMnemonic('E');
        buttonCrypt.setText("Encrypt");
        buttonCrypt.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                buttonCryptActionPerformed(evt);
            }
        });

        buttonDecrypt.setFont(buttonDecrypt.getFont().deriveFont(buttonDecrypt.getFont().getStyle() | Font.BOLD));
        buttonDecrypt.setMnemonic('D');
        buttonDecrypt.setText("Decrypt");
        buttonDecrypt.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                buttonDecryptActionPerformed(evt);
            }
        });

        textPassword.setFont(textPassword.getFont());

        GroupLayout layout = new GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(Alignment.LEADING)
                    .addComponent(scrollOrigin)
                    .addComponent(scrollDestination)
                    .addGroup(Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(labelDestination)
                        .addPreferredGap(ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(buttonCopyDestination))
                    .addGroup(Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(labelAbout, GroupLayout.PREFERRED_SIZE, 162, GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(ComponentPlacement.RELATED, 135, Short.MAX_VALUE)
                        .addComponent(labelPassword)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(textPassword, GroupLayout.PREFERRED_SIZE, 147, GroupLayout.PREFERRED_SIZE)
                        .addGap(18, 18, 18)
                        .addComponent(buttonDecrypt)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(buttonCrypt))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(labelOrigin)
                        .addPreferredGap(ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(buttonClearOrigin)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(buttonPasteOrigin)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(Alignment.BASELINE)
                    .addComponent(labelOrigin)
                    .addComponent(buttonPasteOrigin)
                    .addComponent(buttonClearOrigin))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(scrollOrigin, GroupLayout.PREFERRED_SIZE, 161, GroupLayout.PREFERRED_SIZE)
                .addGap(23, 23, 23)
                .addGroup(layout.createParallelGroup(Alignment.BASELINE)
                    .addComponent(labelDestination)
                    .addComponent(buttonCopyDestination))
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(scrollDestination, GroupLayout.PREFERRED_SIZE, 161, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.RELATED, 21, Short.MAX_VALUE)
                .addGroup(layout.createParallelGroup(Alignment.BASELINE)
                    .addComponent(buttonCrypt)
                    .addComponent(buttonDecrypt)
                    .addComponent(labelPassword)
                    .addComponent(labelAbout)
                    .addComponent(textPassword, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void buttonCryptActionPerformed(ActionEvent evt) {//GEN-FIRST:event_buttonCryptActionPerformed

        char[] passwordArr = textPassword.getPassword();

        String password = new String(passwordArr);
        String plaintext = textAreaOrigin.getText();

        Arrays.fill(passwordArr, '\0');

        if (password.isEmpty()) {
            Dialogs.warning(this, "Password cannot be empty.");
            textPassword.requestFocusInWindow();
            return;
        }

        if (plaintext.isEmpty()) {
            Dialogs.warning(this, "Please provide the plaintext.");
            textAreaOrigin.requestFocusInWindow();
            return;
        }

        try {

            setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            String encryptedMessage = messageCipher.encrypt(plaintext, password);
            if (encryptedMessage == null) {
                Dialogs.error(this, "Could not crypt your message.");
            }
            textAreaDestination.setText(encryptedMessage);
            Dialogs.scrollToTop(textAreaDestination);

        } finally {
            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }

        textPassword.setText(null);

    }//GEN-LAST:event_buttonCryptActionPerformed

    private void buttonDecryptActionPerformed(ActionEvent evt) {//GEN-FIRST:event_buttonDecryptActionPerformed

        char[] passwordArr = textPassword.getPassword();

        String password = new String(passwordArr);
        String ciphertext = textAreaOrigin.getText();

        Arrays.fill(passwordArr, '\0');

        if (password.isEmpty()) {
            Dialogs.warning(this, "Password cannot be empty.");
            textPassword.requestFocusInWindow();
            return;
        }

        if (ciphertext.isEmpty()) {
            Dialogs.warning(this, "Please provide the ciphertext.");
            textAreaOrigin.requestFocusInWindow();
            return;
        }

        try {

            setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            String decryptedMessage = messageCipher.decrypt(ciphertext, password);
            if (decryptedMessage == null) {
                Dialogs.error(this, "Could not decrypt your message.\n"
                        + "Either it is damaged, or you've provided the wrong password.");
            }

            textAreaDestination.setText(decryptedMessage);
            Dialogs.scrollToTop(textAreaDestination);

        } finally {
            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }

        textPassword.setText(null);

    }//GEN-LAST:event_buttonDecryptActionPerformed

    private void labelAboutMouseClicked(MouseEvent evt) {//GEN-FIRST:event_labelAboutMouseClicked
        AboutWindow aboutWindow = new AboutWindow(
                this,
                ProgramProperties.get("name"), 
                ProgramProperties.get("version"),
                ProgramProperties.get("copyright"),
                ProgramProperties.get("website"));
        aboutWindow.setVisible(true);
    }//GEN-LAST:event_labelAboutMouseClicked

    private void formWindowOpened(WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
        textAreaOrigin.requestFocus();
    }//GEN-LAST:event_formWindowOpened

    private void buttonClearOriginActionPerformed(ActionEvent evt) {//GEN-FIRST:event_buttonClearOriginActionPerformed
        textAreaOrigin.setText(null);
        textAreaOrigin.requestFocusInWindow();
    }//GEN-LAST:event_buttonClearOriginActionPerformed

    private void buttonPasteOriginActionPerformed(ActionEvent evt) {//GEN-FIRST:event_buttonPasteOriginActionPerformed

        String clipboardContents = ClipboardManager.getClipboard();

        if (clipboardContents == null || clipboardContents.isEmpty()) {
            Dialogs.warning(this, "Clipboard is empty.");
            return;
        }

        boolean empty = textAreaOrigin.getText().trim().isEmpty();

        if (empty || Dialogs.confirm(this, "Current field contents will be replaced. Continue?")) {
            textAreaOrigin.setText(clipboardContents);
            Dialogs.scrollToTop(textAreaOrigin);
        }

    }//GEN-LAST:event_buttonPasteOriginActionPerformed

    private void buttonCopyDestinationActionPerformed(ActionEvent evt) {//GEN-FIRST:event_buttonCopyDestinationActionPerformed
        ClipboardManager.setClipboard(
                textAreaDestination.getText());
    }//GEN-LAST:event_buttonCopyDestinationActionPerformed

    private void textAreaDestinationFocusGained(FocusEvent evt) {//GEN-FIRST:event_textAreaDestinationFocusGained
        textAreaDestination.selectAll();
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run() {
                Dialogs.scrollToTop(scrollDestination);        
            }
        });
    }//GEN-LAST:event_textAreaDestinationFocusGained
    
    private DocumentListener textAreaListener = new DocumentListener() {

        private void helper() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    textAreaDestination.setText(null);
                }
            });
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            helper();
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            helper();
        }

        @Override
        public void changedUpdate(DocumentEvent e) {
        }
    };
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private JButton buttonClearOrigin;
    private JButton buttonCopyDestination;
    private JButton buttonCrypt;
    private JButton buttonDecrypt;
    private JButton buttonPasteOrigin;
    private JLabel labelAbout;
    private JLabel labelDestination;
    private JLabel labelOrigin;
    private JLabel labelPassword;
    private JScrollPane scrollDestination;
    private JScrollPane scrollOrigin;
    private JTextArea textAreaDestination;
    private JTextArea textAreaOrigin;
    private JPasswordField textPassword;
    // End of variables declaration//GEN-END:variables
}
